//
// Created by denglibin on 19-5-7.
//
#include "shader.h"
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <sys/stat.h>
/**
 * 创建着色器程序
 * @param shader
 */
void createShaderProgram(ShaderProgram * shader){

    //读取顶点着色器代码
    char * vertexSrc = readFile(shader->vertexFilePath);
    if(vertexSrc == NULL){
        return;
    }
    //读取片段着色器代码
    char * fragmentSrc = readFile(shader->fragmentFilePath);
    if(fragmentSrc == NULL){
        free(vertexSrc);
        return;
    }
    //创建顶点着色器
    int vertexShaderId = createShader(VERTEX, vertexSrc);
    //创建片段着色器
    int fragmentShaderId = createShader(FRAGMENT, fragmentSrc);


    // 创建着色器程序对象返回id 着色器程序对象(Shader Program Object)是多个着色器合并之后并最终链接完成的版本
    int shaderProgramId = glCreateProgram();
    //添加着色器到着色器程序对象上 然后链接
    glAttachShader(shaderProgramId, vertexShaderId);
    glAttachShader(shaderProgramId, fragmentShaderId);
    glLinkProgram(shaderProgramId);

    // check for linking errors 检查是否链接错误

    int success;
    char infoLog[512];
    glGetProgramiv(shaderProgramId, GL_LINK_STATUS, &success);
    if (!success) {
        glGetProgramInfoLog(shaderProgramId, 512, NULL, infoLog);
        printf("ERROR::SHADER::PROGRAM::LINKING_FAILED:%s\n",infoLog);
    }
    shader->id = shaderProgramId;
    //删除着色器对象,链接完成后就不需要了
    glDeleteShader(vertexShaderId);
    glDeleteShader(fragmentShaderId);
    //释放代码内存
    free(vertexSrc);
    free(fragmentSrc);
}

/**
 * 设置uniform 变量的值 bool类型
 * @param shaderProgramId  着色器程序id
 * @param name  变量名
 * @param value  值
 */
void setBool(unsigned  int shaderProgramId, GLchar *name,  GLboolean  value){
    glUniform1i(glGetUniformLocation(shaderProgramId, name), (int)value);
}
/**
 * 设置uniform 变量的值 int类型
 * @param shaderProgramId  着色器程序id
 * @param name  变量名
 * @param value  值
 */
void setInt(unsigned  int shaderProgramId, GLchar *name, GLint value){
    glUniform1i(glGetUniformLocation(shaderProgramId, name), value);
}
/**
 * 设置uniform 变量的值 float类型
 * @param shaderProgramId  着色器程序id
 * @param name  变量名
 * @param value  值
 */
void setFloat(unsigned  int shaderProgramId, GLchar *name, GLfloat value){
    glUniform1f(glGetUniformLocation(shaderProgramId, name), value);
}
/**
 * 设置uniform 变量的值 4个 float类型
 * @param shaderProgramId 着色程序id
 * @param name 变量名
 * @param value1
 * @param value2
 * @param value3
 * @param value4
 */
void set4Float(unsigned  int shaderProgramId, GLchar *name,GLfloat value1,GLfloat value2,GLfloat value3,GLfloat value4){
    glUniform4f(glGetUniformLocation(shaderProgramId, name), value1, value2, value3, value4);
}
/**
 *
 * @param shaderProgramId
 * @param name
 * @param value1
 * @param value2
 * @param value3
 */
void setVec3(unsigned int shaderProgramId, GLchar *name, GLfloat value1, GLfloat value2, GLfloat value3){
    glUniform3f(glGetUniformLocation(shaderProgramId, name), value1, value2, value3);
}


/**
 *
 * 读取文件内容到缓冲区
 * @param srcFilePath
 * @return 返回缓冲区指针 失败返回NULL
 */
char * readFile(char * srcFilePath){
    FILE * file = fopen(srcFilePath, "r");
    if(file == NULL){
        printf("顶点着色器代码文件打开失败");
        return NULL;
    }
    int bufferSize = getFileSize(srcFilePath) + 1; //缓冲区大小
    //printf("缓冲区大小:%d\n",bufferSize);
    char * buf = (char *)calloc(sizeof(char), bufferSize); //分配缓冲区
    if(buf == NULL){
        printf("读取文件,分配内存失败");
        fclose(file);
        return NULL;
    }
    while (!feof(file)){
        //将字符读进缓冲区
        fread(buf, 1, bufferSize, file);
    }

    //关闭文件
    fclose(file);
    return buf;
}
/**
 * 创建着色器
 * @param shaderType 着色器类型
 * @param shaderSrc 着色器代码
 * @return 着色器 id 失败返回-1
 */
int createShader(ShaderType shaderType, char * shaderSrc){
    // vertex shader 创建顶点着色器返回id
    int SHADER_TYPE;
    switch (shaderType){
        case VERTEX:
            SHADER_TYPE = GL_VERTEX_SHADER;
            break;
        case FRAGMENT:
            SHADER_TYPE = GL_FRAGMENT_SHADER;
    }
    int shaderId = glCreateShader(SHADER_TYPE);
    //添加着色器源码 glShaderSource函数把要编译的着色器对象作为第一个参数。
    // 第二参数指定了传递的源码字符串数量，这里只有一个。
    // 第三个参数是顶点着色器真正的源码，
    // 第四个参数我们先设置为NULL
    glShaderSource(shaderId, 1, &shaderSrc, NULL);
    //编译着色器
    glCompileShader(shaderId);
    // 检查是否成功
    int success;
    char infoLog[512];
    //检查是否编译成功
    glGetShaderiv(shaderId, GL_COMPILE_STATUS, &success);
    if (!success)
    {
        glGetShaderInfoLog(shaderId, 512, NULL, infoLog);
        printf("%s着色器编译错误:%s\n",shaderType == VERTEX? "顶点着色器":"片段着色器",infoLog);
        printf("代码:\n%s", shaderSrc);
        return -1;
    }
    return shaderId;
}
/**
 * 获取文件大小
 * @param filePath
 * @return
 */
int getFileSize(char *filePath){
        struct stat stat_buf;
        stat(filePath,&stat_buf);
        int size=stat_buf.st_size;
        return size;
}